library exchangerates;
{}

{$R *.res}
{.$R 'cx_options.res' 'cx_options.rc'}

uses
  m_globaldefs in 'api\m_globaldefs.pas',
  FreeImage in 'api\FreeImage.pas',
  m_api in 'api\m_api.pas',
  dbsettings in 'api\dbsettings.pas',
  common in 'api\common.pas',
  Windows,
  Classes,
  DateUtils,
  SysUtils,
  StrUtils,
  cx_vars in 'cx_vars.pas',
  cx_procs in 'cx_procs.pas',
  cx_http in 'cx_http.pas',
  cx_options_new in 'cx_options_new.pas',
  cx_CBR.ru in 'cx_CBR.ru.pas',
  cx_google in 'cx_google.pas',
  cx_contacts in 'cx_contacts.pas',
  cx_XML in 'cx_XML.pas',
  cx_timer in 'cx_timer.pas',
  cx_currencies in 'cx_currencies.pas',
  Tokens in 'Tokens.pas',
  IO in 'io.pas',
  cx_nbrb_by in 'cx_nbrb_by.pas';

{$include api/m_helpers.inc}
{$include api/m_flags.inc}

var
  //Query,Response  : TStringList;
  //Currency : String;
  //cx_hAddToList,
  cx_hLoadIcon,
  cx_hGetName,
  cx_hGetCaps,
  cx_hSetStatus,
  cx_hGetStatus : THandle;


//function cx_OnOptInit(wParam: WPARAM; lParam: LPARAM): Integer; cdecl; forward;
//function cx_OnNewOptInit(wParam: WPARAM; lParam: LPARAM): Integer; cdecl; forward;
function OnModulesLoad(wParam,lParam:DWord): Integer; cdecl; forward;
function cx_LoadIcon (wParam: wParam; lParam: lParam): Integer; cdecl; forward;
function cx_GetName(wParam: wParam; lParam: lParam): Integer; cdecl; forward;
function cx_GetCaps(wParam: wParam; lParam: lParam): Integer; cdecl; forward;
function cx_SetStatus(wParam: wParam; lParam: lParam): Integer; cdecl; forward;
function cx_GetStatus(wParam: wParam; lParam: lParam): Integer; cdecl; forward;

Const

  PluginInfo: TPLUGININFO = (
    cbSize: SizeOf(TPLUGININFO);
    shortName: piShortName;
    version: piVersion;
    description: piDescription;
    author: piAuthor;
    authorEmail: piAuthorEmail;
    copyright: piCopyright;
    homepage: piHomepage;
    flags: UNICODE_AWARE;
    replacesDefaultModule: 0;
  );
  (*
  PluginInfoEx: TPLUGININFOEX = (
    cbSize: SizeOf(TPLUGININFOEX);
    shortName: piShortName;
    version: piVersion;
    description: piDescription;
    author: piAuthor;
    authorEmail: piAuthorEmail;
    copyright: piCopyright;
    homepage: piHomepage;
    flags: UNICODE_AWARE;
    replacesDefaultModule: 0;
    uuid: '{30B86B40-9B88-4388-8626-7C2B88EA2188}';
  );
  *)
function MirandaPluginInfo(mirandaVersion: DWORD): PPLUGININFO; cdecl;
begin
 Result := @PluginInfo;
 //PluginInfo.cbSize:=SizeOf(TPLUGININFO);
end;

{
function MirandaPluginInfo(mirandaVersion: DWORD): PPLUGININFOEX; cdecl;
begin
 Result := @PluginInfoEx;
 PluginInfoEx.cbSize:=SizeOf(TPLUGININFO);
end;
}
(*
//    It is said that this function should be for 0.8 support.
function MirandaPluginInfoEx(mirandaVersion: DWORD): PPLUGININFOEX; cdecl;
begin
 Result := @PluginInfoEx;
 //PluginInfoEx.cbSize:=SizeOf(TPLUGININFOEX);
 //PluginInfoEx.uuid  :=PluginInfoEx.uuid;
end;
*)
    {
    function cx_OnOptInit(wParam: WPARAM; lParam: LPARAM): Integer; cdecl;
    var
      cx_odp: TOPTIONSDIALOGPAGE;
    begin
      ZeroMemory(@cx_odp,SizeOf(cx_odp));
      cx_odp.cbSize := sizeof(cx_odp);
      cx_odp.Position := 0;
      cx_odp.hInstance := hInstance;
      cx_odp.pszTemplate := MakeIntResource(CX_IDD_OPT);
      cx_odp.szTitle.a := Translate(piShortName);
      cx_odp.szGroup.a := nil;
      cx_odp.pfnDlgProc := @cx_OptDialogProc;
      cx_odp.flags := ODPF_BOLDGROUPS;
      PluginLink^.CallService(MS_OPT_ADDPAGE,wParam,DWORD(@cx_odp));
      Result:=0;
    end;
    }

procedure AddMainMenuItems;
 var
  cx_mi: TCListMenuItem;  // woz here
begin
 FillChar(cx_mi, sizeof(cx_mi), 0);
 cx_mi.cbSize := sizeof(cx_mi);
 cx_mi.position := $7FFFFFFE;
 cx_mi.flags := 0;
 //cx_mi.hIcon := LoadSkinnedIcon(SKINICON_OTHER_MIRANDA);
 cx_mi.hIcon := LoadSkinnedProtoIcon(piShortName, ID_STATUS_ONLINE);
 //cx_mi.hIcon := LoadSkinnedProtoIcon(piShortName, ID_STATUS_OCCUPIED);
 cx_mi.szName.a := Translate(cx_menu_UpdateRates);
 cx_mi.pszService := piShortName+'/UpdateExchanges';
 cx_hMainMenuItem := pluginLink^.CallService(MS_CLIST_ADDMAINMENUITEM, 0, lParam(@cx_mi));
 FillChar(cx_mi, sizeof(cx_mi), 0);
end;

procedure AddContactMenuItems;
 var
  cx_contact_mi : TCListMenuItem;
begin
 FillChar(cx_contact_mi, sizeof(cx_contact_mi), 0);
 cx_contact_mi.cbSize := sizeof(cx_contact_mi);
 cx_contact_mi.position := $7FFFFFFF;
 cx_contact_mi.flags := 0;
 //cx_contact_mi.hIcon := LoadSkinnedIcon(SKINICON_OTHER_MIRANDA);
 cx_contact_mi.hIcon := LoadSkinnedProtoIcon(piShortName, ID_STATUS_ONLINE);
 //cx_contact_mi.hIcon := LoadSkinnedProtoIcon(piShortName, ID_STATUS_OCCUPIED);
 cx_contact_mi.szName.a := Translate(cx_menu_UpdateRate);
 cx_contact_mi.pszService := piShortName+'/UpdateSingleExchange';
 cx_hContactMenuItem := pluginLink^.CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, lParam(@cx_contact_mi));
 FillChar(cx_contact_mi, sizeof(cx_contact_mi), 0);
end;

function OnModulesLoad(wParam, lParam: DWord): Integer; cdecl;
 begin
  HTTP_NL_Init();
  //
  AddMainMenuItems;
  //
  AddContactMenuItems;
  //
  InitXMLw(cx_XMLw);
  //
  {
  //cx_IsFlags:=Boolean(PluginLink^.ServiceExists(MS_FLAGS_CREATEMERGEDFLAGICON));
  cx_IsFlags:=Boolean(PluginLink^.ServiceExists(MS_FLAGS_LOADFLAGICON));
  MessageBox(0, PChar(BoolToStr(cx_IsFlags, true)), piShortName, MB_OK or MB_ICONINFORMATION);
  MessageBox(0, PChar(BoolToStr(Boolean(PluginLink^.ServiceExists(MS_FLAGS_CREATEMERGEDFLAGICON)), true)), piShortName, MB_OK or MB_ICONINFORMATION);
  }
  //
  Result:=0;
 end;

procedure TimerCallBack(hWnd: HWND; Message: Word; TimerID: Word; SysTime: LongInt); stdcall;
begin
 if Assigned(ThreadUpdate) then
  begin
   if ThreadUpdate.IsTerminated then
    begin
     FreeAndNil(ThreadUpdate);
     ThreadUpdate := TThreadUpdate.Create();
    end;
  end
 else
   ThreadUpdate:=TThreadUpdate.Create();
end;

function UpdateSingleExchange(wParam: WPARAM; lParam: LPARAM): Integer; cdecl;
begin
 //ThreadSingleUpdate := TThreadSingleUpdate.Create(wParam);
 if Assigned(ThreadUpdate) then
  begin
   ThreadUpdate.WaitFor;
   FreeAndNil(ThreadUpdate);
  end;
 ThreadUpdate:=TThreadUpdate.Create(wParam);
 Result:=0;
end;

function OnMainMenu(hContact:WPARAM; lParam:LPARAM): int; cdecl;
var
 mi: TCListMenuItem;
begin
 FillChar(mi,SizeOf(mi),0);
 mi.cbSize:=sizeof(mi);
 //
 if cx_Status=ID_STATUS_OFFLINE then
  begin
   mi.szName.a:=Translate(cx_menu_UpdateRates);
   mi.hIcon := LoadSkinnedProtoIcon(piShortName, ID_STATUS_ONLINE);
   mi.flags:=CMIF_GRAYED or CMIM_NAME or CMIM_ICON or CMIM_FLAGS
  end
 else
  begin
   if Assigned(ThreadUpdate) then
    begin
     if ThreadUpdate.IsTerminated then
      begin
       mi.szName.a:=Translate(cx_menu_UpdateRates);
       mi.hIcon := LoadSkinnedProtoIcon(piShortName, ID_STATUS_ONLINE);
       mi.flags:=CMIM_NAME or CMIM_ICON or CMIM_FLAGS
      end
     else
      begin
       mi.szName.a:=Translate(cx_menu_UpdateStop);
       mi.hIcon := LoadSkinnedProtoIcon(piShortName, ID_STATUS_OCCUPIED);
       mi.flags:=CMIM_NAME or CMIM_ICON or CMIM_FLAGS;
      end;
    end
   else
    begin
     mi.szName.a:=Translate(cx_menu_UpdateRates);
     mi.hIcon := LoadSkinnedProtoIcon(piShortName, ID_STATUS_ONLINE);
     mi.flags:=CMIM_NAME or CMIM_ICON or CMIM_FLAGS;
    end;
  end;
 PluginLink^.CallService(MS_CLIST_MODIFYMENUITEM, cx_hMainMenuItem, dword(@mi));
 result:=0;
end;

function OnContactMenu(hContact:WPARAM; lParam:LPARAM): int; cdecl;
var
 mi: TCListMenuItem;
begin
 FillChar(mi,SizeOf(mi),0);
 mi.cbSize:=sizeof(mi);
 if IsContactProto(hContact, piShortName)<>-1 then
   mi.flags:=CMIF_NOTOFFLINE or CMIF_NOTOFFLIST or CMIM_FLAGS or CMIF_HIDDEN
 else
  begin
   if cx_Status=ID_STATUS_OFFLINE then
     mi.flags:=CMIF_NOTOFFLIST or CMIF_GRAYED or CMIM_FLAGS
   else
    begin
     if Assigned(ThreadUpdate) then
      begin
       if ThreadUpdate.IsTerminated then
         mi.flags:=CMIF_NOTOFFLIST or CMIM_FLAGS
       else
         mi.flags:=CMIF_NOTOFFLIST or CMIF_GRAYED or CMIM_FLAGS
      end
     else
       mi.flags:=CMIF_NOTOFFLIST or CMIM_FLAGS;
    end;
  end;
 PluginLink^.CallService(MS_CLIST_MODIFYMENUITEM, cx_hContactMenuItem, dword(@mi));
 result:=0;
end;

function PluginMenuCommand(wParam: WPARAM; lParam: LPARAM): Integer; cdecl;
begin
  cx_UpdateRates(true);
  Result:=0;
end;

//--- adapted from Weather's LoadIconEx & ReleaseIconEx ---
{
function LoadIconFromIcoLib(Path: String): THandle;
 var
  szIconName: PChar;
begin
 FillChar(szIconName, Length(Path)+1, #0);
 StrPCopy(szIconName, Path);
 Result:=PluginLink^.CallService(MS_SKIN2_GETICON, 0, DWORD(szIconName));
end;

procedure ReleaseIconFromIcoLib(Path: String);
 var
  szIconName: PChar;
begin
 FillChar(szIconName, Length(Path)+1, #0);
 StrPCopy(szIconName, Path);
 PluginLink^.CallService(MS_SKIN2_RELEASEICON, 0, DWORD(szIconName));
end;
}
//---

// Hooks
procedure CreateHooks;
begin
 //cx_hkOptInitialise := PluginLink^.HookEvent(ME_OPT_INITIALISE, cx_OnOptInit);
 cx_hkOptInitialise := PluginLink^.HookEvent(ME_OPT_INITIALISE, cx_OnNewOptInit);
 cx_hkModulesLoad := pluginLink^.HookEvent(ME_SYSTEM_MODULESLOADED, @OnModulesLoad);
 cx_hkOnMainMenu:= pluginLink^.HookEvent(ME_CLIST_PREBUILDMAINMENU, @OnMainMenu);
 cx_hkOnContactMenu := pluginLink^.HookEvent(ME_CLIST_PREBUILDCONTACTMENU, @OnContactMenu);
 {
 cx_hHookExtraRebuild := pluginLink^.HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, cx_ExtraListRebuild);
 cx_hHookExtraApply := pluginLink^.HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, cx_ExtraImageApply);
 }
end;

procedure CloseHooks;
begin
 {
 PluginLink^.UnhookEvent(cx_hHookExtraApply);
 PluginLink^.UnhookEvent(cx_hHookExtraRebuild);
 }
 PluginLink^.UnhookEvent(cx_hkOnContactMenu);
 PluginLink^.UnhookEvent(cx_hkOnMainMenu);
 PluginLink^.UnhookEvent(cx_hkModulesLoad);
 PluginLink^.UnhookEvent(cx_hkOptInitialise);
end;
//

function Load(link: PPLUGINLINK): int; cdecl;
var
  cx_pd: TProtocolDescriptorOld;
begin
  //---------Standard part start----------------
  // this line is VERY VERY important, if it's not present, expect crashes.
  //PLUGINLINK := Pointer(link);
  PLUGINLINK := PPLUGINLINK(link);
  Result := 0;
  //
  InitMMI; //!!! Fuck you Spielberg !!!
  //
  pluginLink^.CreateServiceFunction(piShortName+'/UpdateExchanges', @PluginMenuCommand);
  //
  pluginLink^.CreateServiceFunction(piShortName+'/UpdateSingleExchange', @UpdateSingleExchange);
  //---------Standard part end------------------
  //---------Options dialog initialization-----
  CreateHooks;
  //-----Registering as a protocol
  FillChar(cx_pd, sizeof(cx_pd), 0);
  cx_pd.cbSize := sizeof(cx_pd);
  cx_pd.szName := piShortName;
  cx_pd._type := PROTOTYPE_PROTOCOL;
  pluginLink^.CallService(MS_PROTO_REGISTERMODULE, 0, lParam(@cx_pd));
  cx_hGetName := CreateProtoServiceFunction(piShortName, PS_GETNAME, cx_GetName);
  cx_hLoadIcon := CreateProtoServiceFunction(piShortName, PS_LOADICON, cx_LoadIcon);
  cx_hGetCaps := CreateProtoServiceFunction(piShortName, PS_GETCAPS, cx_GetCaps);
  cx_hSetStatus := CreateProtoServiceFunction(piShortName, PS_SETSTATUS, cx_SetStatus);
  cx_hGetStatus := CreateProtoServiceFunction(piShortName, PS_GETSTATUS, cx_GetStatus);
  //
  DecimalSeparator:='.';
  //
  TimerProc:=@TimerCallBack;
  //
  FillCurrList;
  {
  //
  LoadRates;
  }
end; //--------End of Load function-----------------


// =============================================================================
// function to change the status and notifies everybody
// -----------------------------------------------------------------------------
function cx_SetStatus(wParam: wParam; lParam: lParam): Integer; cdecl;
begin
  if wParam = cx_Status then
  begin
     result := 0;
     exit;
  end;
  cx_StatusPrevious := cx_Status;
  cx_Status := wParam;
  // we don't support all statuses, but Miranda may try to
  // setup unsupported status (for ex., when user presses Ctrl+0..9
  // so, we should have the following line
  if (cx_Status = ID_STATUS_AWAY) or
     (cx_Status = ID_STATUS_DND) or
     (cx_Status = ID_STATUS_NA) or
     (cx_Status = ID_STATUS_OCCUPIED) or
     (cx_Status = ID_STATUS_FREECHAT) or
     (cx_Status = ID_STATUS_ONTHEPHONE) or
     (cx_Status = ID_STATUS_OUTTOLUNCH) then
   begin
     cx_Status := ID_STATUS_ONLINE;
   end;

  if cx_Status = ID_STATUS_ONLINE then
    begin
     ResetTimer;
     //
     cx_UpdateRates;
    end;

  if cx_Status = ID_STATUS_OFFLINE then
   begin
    ThreadUpdate.Terminate;
    ThreadUpdate.WaitFor;
    FreeAndNil(ThreadUpdate);
    //
    CloseTimer;
    //
    cx_RefreshContacts(false);
   end;

  Result := 0;
end;

// =============================================================================
// function to return the current status
// -----------------------------------------------------------------------------
function cx_GetStatus(wParam: wParam; lParam: lParam): Integer; cdecl;
begin
  Result := cx_Status;
end;

// =============================================================================
// function to identify list of functions supported by the plugin
// -----------------------------------------------------------------------------
function cx_GetCaps(wParam: wParam; lParam: lParam): Integer; cdecl;
begin
  Case wParam Of
    (*
    PFLAGNUM_1:
      Result := PF1_SEARCHBYNAME or // protocol supports searching by nick/first/last names
      PF1_IM or // supports IM sending & receiving
      PFLAGNUM_1 or // will get authorisation requests for some or all contacts
      // PF1_NUMERICUSERID; // the unique user IDs for this protocol are numeric
      PF1_SERVERCLIST; // contact lists are stored on the server, not locally
    *)
    PFLAGNUM_2:
      Result := PF2_ONLINE or PF2_LONGAWAY or PF2_LIGHTDND or PF2_INVISIBLE; // list of statuses supported, just online & offline is required

    PFLAGNUM_5:
      Result := PF2_LONGAWAY or PF2_LIGHTDND or PF2_INVISIBLE;

    // PFLAGNUM_3 :
    // Result := PF2_ONLINE or PF2_LONGAWAY;
   (*
   PFLAGNUM_4:
      Result := $00000100 or // PF4_IMSENDOFFLINE; protocol is able to send offline messages
                PF4_AVATARS; // avatars supported
   *)
   PFLAG_UNIQUEIDTEXT:
     Result := Integer(Translate('ID')); // returns a static buffer of text describing the unique field by which this protocol identifies users
                                         // this name will be used for ex. in BASIC SEARCH

    PFLAG_MAXLENOFMESSAGE:
      Result := 2000;  // maximum length of one message

    PFLAG_UNIQUEIDSETTING:
      Result := Integer(PChar('ID')); // returns the DB setting name that has the ID which makes this user unique on that system
  Else
    Result := 0;
  End;
end;


    function cx_GetName(wParam: wParam; lParam: lParam): Integer; cdecl;
    begin
      StrLCopy(PChar(lParam), piShortName, wParam);
      Result := 0;
    end;

    function cx_LoadIcon (wParam: wParam; lParam: lParam): Integer; cdecl;
    var
      id: string; // to store id of icon to be loaded
      cx: int;
      cy: int;
    begin
      // regardless of the code below, this function is called only when
      // wParam = PLI_PROTOCOL and it is required to display proto icon
      // in Miranda's main menu -> Status

      case (wParam and $ffff) of
        PLI_PROTOCOL: id := 'ICON_PROTO'; // icon representing protocol
        PLI_ONLINE: id := 'ICON_ONLINE'; // online state icon for the protocol
        PLI_OFFLINE: id := 'ICON_OFFLINE'; // offline state icon for the protocol
      else
        Result := 0;
        Exit;
    end;

   if (bool(wParam and PLIF_SMALL)) then
      begin
        // if small icon - 16x16 by default
        cx := SM_CXSMICON;
        cy := SM_CYSMICON;
      end
      else
      begin
        // if big icon - 32x32 by default
        cx := SM_CXICON;
        cy := SM_CYICON
      end;

  // Result := LoadImage(hInstance, MAKEINTRESOURCE('ICON_PROTO'), IMAGE_ICON, 16, 16, 0);
  // Result := PluginLink^.CallService(MS_SKIN2_GETICON, 0, Windows.lparam(vk_icon_note));
  // Result := LoadSkinnedIcon(SKINICON_OTHER_MIRANDA);
  // Result := CopyIcon(CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)iconList[i].hIconLibItem));

    Result := Int(LoadImage(hInstance,
    MAKEINTRESOURCE(PChar(id)),
    IMAGE_ICON,
    GetSystemMetrics(cx),
    GetSystemMetrics(cy),
    0));
  end;



    function Unload: int; cdecl;
    begin
     {
     if cx_MenuIcon<>0 then
       ReleaseIconFromIcoLib(Format(cx_MenuIconPath, [piShortName]));
     }
     SetLength(cx_Clist, 0);

     CloseTimer;

     CloseHooks;

     HTTP_NL_Close();

     Result := 0;

     //XML.Destroy;
    end;

    exports
    MirandaPluginInfo,
    //MirandaPluginInfoEx,
    Load, Unload;

begin
end.
